#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__date__ = "20 août 2010"
__author__ = "guillaume"
__all__ = [u'ShellCmds', u'ScriptCommand', u'ShellScriptWriter',
           u'find_program']

import os

from _pylot.minimal.utils import (Utils, check_type, check_iterable, mkdtempu,
                                CREATED_PATHS)
from _pylot.minimal.commandgroup import ShellCommand
from _pylot.minimal.path import Path
from _pylot.minimal.system import ENCODING

class ShellCmds (object):
  u"""
  .. todo :: 
    split in CmdInterface, ShellCmd, CMakeCmd
  """
  created_paths = CREATED_PATHS

  @classmethod
  def makeDir(cls, directory):
    u"""
    Makes directory. This directory can be removed by 
    :meth:`~pylot.core.ShellCmds.eraseSafe`
    """
    check_type(u'directory', directory, Path)

    ShellCmds.created_paths.add(directory.abspath())
    return ShellCommand(u'mkdir', [u'-p', directory.ushellpath()],
                        alias=u'make dir %s' % directory)

  @classmethod
  def safeRemove (cls, directory, verbose=True, force=False):
    u"""
    Like :meth:`ShellManager.eraseSafe<pylot.core.ShellManager.eraseSafe>` but done in shell 
    script, not in python
    """
    check_type(u'directory', directory, Path)
    check_type(u'verbose', verbose, bool)
    check_type(u'force', force, bool)

    path = directory.abspath()

    if verbose : args = u'vf'
    else : args = u'f'

    if path in ShellCmds.created_paths and force is True :
      ShellCmds.created_paths.remove(path)
      return ShellCommand(u'rm', [u'-r%s' % args, path.shellpath()],
                          alias=u'remove %s' % path)
    elif path in ShellCmds.created_paths and force is False :
      if path.isdir() : return path.listdir()
      else : return [path]
    else :
      return ShellCommand(u'echo',
        [u'"Path %s not deleted, because not created by pylot"' % path])

  @classmethod
  def remove (cls, directory, force=False):
    u"""
    Removes without safety. Not safe ! No warranty !
    """
    check_type(u'directory', directory, Path)
    check_type(u'force', force, bool)

    path = directory.abspath()
    if force is True :
      if path.isdir() :
        return ShellCommand(u'rm', [u'-rf', path.shellpath()],
          alias=u'remove %s' % path)
      else :
        return ShellCommand(u'rm', [u'-f', path.shellpath()],
          alias=u'remove %s' % path)
    else :
      if path.isdir() : return path.listdir()
      else : return [path]

  @classmethod
  def _cmdCopyMove(cls, cmd, src, dest, opts):
    u"""
    "cmd" recursively src to dest
    """
    assert(isinstance(src, Path))
    assert(isinstance(dest, Path))

    ShellCmds.created_paths.add(dest.abspath())
    return ShellCommand(cmd, [opts, src.shellpath(), dest.shellpath()],
                alias=u' '.join([cmd, unicode(src), unicode(dest)]))

  @classmethod
  def copy(cls, src, dest, force=True):
    check_type(u'src', src, Path)
    check_type(u'dest', dest, Path)
    check_type(u'force', force, bool)

    if force : opts = u'-rf'
    else : opts = u'-r'
    return cls._cmdCopyMove(u'cp', src, dest, opts)

  @classmethod
  def move(cls, src, dest, force=True):
    check_type(u'src', src, Path)
    check_type(u'dest', dest, Path)
    check_type(u'force', force, bool)


    if force : opts = u'-f'
    else : opts = u''
    if src == dest :
      msg = tr_(u'"No move because source and destination are identical"')
      return ShellCommand(u'echo', args=[msg])
    else :
      return cls._cmdCopyMove(u'mv', src, dest, opts)

class ScriptCommand (object):
  def __init__ (self, cmd, args, main=False):
    check_type(u'cmd', cmd, unicode)
    check_iterable(u'args', args)
    check_type(u'main', main, bool)

    self.cmd = cmd
    self.args = args
    self.main = main

  def __unicode__ (self):
    u"""
    >>> ci = ScriptCommand(u'ls', [u'-r', u'/home'])
    >>> unicode(ci)
    u'ls -r /home'
    """
    return u'%s %s' % (self.cmd, u' '.join([unicode(arg) for arg in self.args]))

  def __str__ (self):
    return unicode(self).encode(ENCODING)

class ShellScriptWriter (object):
  u"""
  Eases script writing.
  Use this class only to WRITE script FILE
  Do not use this Class if you want to execute commands.

  How to use it :

  1. create a script object
  2. add commands and special directives
  3. write script to file

  >>> from pyLot.core import (Path, mkdtempu, ShellScriptWriter)
  >>> tmpdir = mkdtempu()
  
  >>> script = ShellScriptWriter()
  >>> script.addCmd(u'ls', [u'-R', u'/'])
  >>> script.addCmd(u'cat', [u'/etc/hosts'])
  >>> script_path = script.toFile(Path(tmpdir, u'test.sh'))
  
  >>> tmpdir.rmtree()

  .. todo :: 
    split in ScripWriterInterface and Bash
  """
  def __init__ (self):
    self.cmds = []
    self.created_paths = CREATED_PATHS

  def newScript (self):
    self.cmds = []

  def toFile(self, filename):
    check_type(u'filename', filename, [Path, unicode])

    cmds = [unicode(cmd) for cmd in self.cmds]
    openfile = Utils.writefile(filename, u'force', u'w')
    openfile.write(u'\n'.join(cmds))
    openfile.write(u'\n')
    openfile.close()
    return filename

  def _abspath (self, path):
    check_type(u'path', path, [Path, unicode])

    return Path(path).abspath()

  def addHeader (self):
    pass

  def addCmd (self, cmd, args=[], main=False) :
    u"""
    :param cmd: Command
    :type cmd: str

    :param args: command args
    :type args: list of str

    :param main: define if this command is an important command
    (used for return code). More than one command can be considered as main command
    :type main: bool
    """
    check_type(u'cmd', cmd, unicode)
    check_iterable(u'args', args)
    check_type(u'main', main, bool)

    command = ScriptCommand(unicode(cmd), args, main)
    self.cmds.append(command)

  def resetCmds(self):
    for i in range(len(self.cmds)) :
      tmp = self.cmds.pop()

def find_program (prognames, binpaths=[]):
  u"""
  Searches programs with name 'prognames' in standard binary path (PATH) and 
  user paths (param binpaths)
  The first occurence is returned. The returned value is the shortest path :
  If program is in standard path, only name is returned, else absolute path.
  If program isn't found, return None

  >>> find_program(u'echo')
  u'echo'
  >>> path = find_program([u'pylot-install',u'pylot-install.py'])
  >>> path #doctest: +ELLIPSIS
  u'...pylot-install'

  :param prognames: Name of programme you want to search. Can be a list
  :type prognames: list or str
  """
  raise NotImplementedError
#  io = process_handler.io
#  display_level = io.level
#  io.level = u'silent'
#  if isinstance(prognames, (list, tuple, set)) :
#    prognames = list(prognames)
#  else :
#    prognames = [prognames]
#
#  script = ShellScriptWriter()
#  # first search if program is in PATH (use which)
#  tmpdir = mkdtempu()
#  for name in prognames :
#    script.resetCmds()
#    script.addCmd(u'which %s > /dev/null 2> /dev/null' % name, main=True)
#    script_path = script.toFile(Path(tmpdir, u'find_program.sh'))
#    script_path.chmod(0755)
#    process = process_handler.runCmd(script_path.shellpath(), wait=True)
#    tmpdir.rmtree()
#    if process.return_code == 0 :
#      io.level = display_level
#      return name
#
#  #if here : means which fail, so we try all bin paths
#  binpaths = set(binpaths)
#  binpaths.add(Path(u'.').abspath())
#  try :
#    PATH = os.environ[u'PATH']
#  except KeyError :
#    pass
#  else :
#    paths = PATH.split(u':')
#    for p in paths : binpaths.add(p)
#  binpaths.add(u'/usr/bin')
#  binpaths.add(u'/usr/local/bin')
#
#  for name in prognames :
#    for path in binpaths :
#      program = Path(path, name)
#      if program.isfile() :
#        io.level = display_level
#        return program.abspath()
#
#  io.level = display_level
